/*
 * Copyright (c) 2023 Huawei Technologies Co., Ltd.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *         http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#include "smbus.h"


namespace dev {
    smbus::smbus(mc::engine::abstract_object* parent) 
        : std_smbus(parent) {
    }

    smbus::~smbus() {
    }

    void smbus::init(mc::mutable_dict& params) {
        // 继承自父类方法
        m_buffer_len = params["BufferLen"].as<uint32_t>();
        m_mcu_address = params["McuAddress"].as<uint32_t>();

        elog("smbus init, buffer len: ${buffer_len}, mcu address: ${mcu_address}", ("buffer_len", m_buffer_len)("mcu_address", m_mcu_address));
    }

    void smbus::register_WriteRead_method(std::function<std::vector<uint8_t>(const std::vector<uint8_t>&, uint32_t)> func) {
        m_WriteRead_func = func;
        ilog("WriteRead method registered successfully");
    }

    uint32_t smbus::get_max_frame_len() {
        return std_smbus::get_max_frame_len() - crc32_len;
    }

    std::pair<bool, std::vector<uint8_t>> smbus::send_and_receive(const std::string& data, uint32_t len) {
        try {
            // 检查WriteRead函数是否已注册
            if (!m_WriteRead_func) {
                elog("Error: WriteRead function is not registered");
                return {false, std::vector<uint8_t>()};
            }
            // 调用refchip的WriteRead方法 , 但是data得转换为vector<uint8_t>
            std::vector<uint8_t> data_vec(data.begin(), data.end());
            auto rsp = m_WriteRead_func(data_vec, len);
            return {true, rsp};
        } catch (const std::exception& e) {
            elog("error in smbus::send_and_receive, error: ${error}", ("error", e.what()));
            return {false, std::vector<uint8_t>()};
        }
    }

    std::string smbus::construct_request_data(mc::mutable_dict& track_request) {
        mc::mutable_dict request = mc::mutable_dict{{"reserved", 0}};
        request["opcode"] = track_request["opcode"];
        request["offset"] = track_request["data_written"];
        request["length"] = track_request["len"];

        auto temp = pack_smbus_request(request);
        // 计算crc32 
        auto crc = mc::crypto::CRC32().calculate(temp, 0, true);
        // 将crc 转换为I4 字节序
        std::string crc_bytes;
        crc_bytes.append(reinterpret_cast<const char*>(&crc), sizeof(crc));
        std::string result = temp + crc_bytes;
        return result;
    }

    std::string smbus::pack_smbus_request(const mc::mutable_dict& request) {
        smbus_request_t smbus_req;
        smbus_req.reserved = request["reserved"].as<uint16_t>();
        smbus_req.opcode = request["opcode"].as<uint16_t>();
        smbus_req.offset = request["offset"].as<uint32_t>();
        smbus_req.length = request["length"].as<uint32_t>();

        std::string result;
        result.append(reinterpret_cast<const char*>(&smbus_req.reserved), sizeof(smbus_req.reserved));
        result.append(reinterpret_cast<const char*>(&smbus_req.opcode), sizeof(smbus_req.opcode));
        result.append(reinterpret_cast<const char*>(&smbus_req.offset), sizeof(smbus_req.offset));
        result.append(reinterpret_cast<const char*>(&smbus_req.length), sizeof(smbus_req.length));
        return result;
    }

    std::pair<bool, mc::mutable_dict> smbus::unpack_response_data(const std::string& rsp_bin, const int32_t expect_data_len) {
        int32_t M = m_buffer_len;
        if (expect_data_len > 0 ) {
            M = expect_data_len + header_len + crc32_len;
        }
        elog("M is ${M} and buffer len is ${buffer_len}", ("M", M)("buffer_len", m_buffer_len));
        std::string bin_data = rsp_bin.substr(0, M - crc32_len);
        std::string crc_bin = rsp_bin.substr(M - crc32_len, crc32_len);
        auto crc_cal = mc::crypto::CRC32().calculate(bin_data, 0, true);
        // 将crc 转换为I4 字节序
        std::string crc_expect;
        crc_expect.append(reinterpret_cast<const char*>(&crc_cal), sizeof(crc_cal));

        if (crc_expect != crc_bin) {
            return {false, mc::mutable_dict{{"error_msg", "crc check error on smbus response"}}};
        }
        auto response = _unpack_response(bin_data);
        return {true , response};
    }

    mc::mutable_dict smbus::_unpack_response(const std::string& rsp_bin) {
        mc::mutable_dict response;
        uint16_t error_code = static_cast<uint8_t>(rsp_bin[0]) | (static_cast<uint8_t>(rsp_bin[1]) << 8);
        response["error_code"] = error_code;

        uint16_t opcode = static_cast<uint8_t>(rsp_bin[2]) | (static_cast<uint8_t>(rsp_bin[3]) << 8);
        response["opcode"] = opcode;
        
        uint32_t total_length = static_cast<uint8_t>(rsp_bin[4]) | (static_cast<uint8_t>(rsp_bin[5]) << 8) | (static_cast<uint8_t>(rsp_bin[6]) << 16) | (static_cast<uint8_t>(rsp_bin[7]) << 24);
        response["total_length"] = total_length;

        uint32_t length = static_cast<uint8_t>(rsp_bin[8]) | (static_cast<uint8_t>(rsp_bin[9]) << 8) | (static_cast<uint8_t>(rsp_bin[10]) << 16) | (static_cast<uint8_t>(rsp_bin[11]) << 24);
        response["length"] = length;

        response["data"] = rsp_bin.substr(12, length);

        return response;
    }


    std::pair<bool, std::vector<uint8_t>> smbus::send_and_receive_request_in_frames(mc::mutable_dict& track_request) {
        auto data_written = track_request["data_written"].as<uint32_t>();
        auto data = track_request["data"].as<std::string>();
        
        if (!data.empty()) {
            auto max_frame_len = track_request["max_frame_len"].as<uint32_t>();
            if ((data_written + max_frame_len) > data.length()) {
                track_request["len"] = data.length() - data_written;
            }
        }

        auto req_bin = construct_request_data(track_request);
        if (req_bin.empty()) {
            return {false, std::vector<uint8_t>()};
        }

        return send_and_receive(req_bin, m_buffer_len);
    }

}